/*
 *  Copyright (C) 2007 Veit Wehner.
 *  All Rights Reserved.
 *
 *  This is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This software is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 */




 /** \file flash.c
  * \brief This file contains the function for accessing the 
  *  external flash and for writing the program memory.
  * \author Veit Wehner <veit.wehner@gmail.com>
  */
         

#include "sw_update.h"

OS_DECLARE_EXTERN_RESOURCE(UART_RESOURCE);
OS_DECLARE_EXTERN_RESOURCE(FLASH_RESOURCE);

/** This function checks whether her caller is running from 
 *   RAM or Flash Memory by inspecting the Program Counter
 */
inline  char __attribute__((always_inline))running_from_ram(){
  unsigned short pc_count;
  asm("mov r0, %0": "=m" (pc_count));
  return (pc_count>0x38FF)? 0 : 1; 
}




/** This function finds a location in RAM where the code of the reprogramming 
*   routine is loaded into. Therefore, the stack pointer (SP) is evaluated. If there is enough 
*   place below the SP it return the SP plus some byte. Otherwise, The begin of the RAM is returned. 
*   Note: Ram is growing towards the low addresses.
*   @return Returns the pointer to the place in RAM where enough place is found.
*/

inline unsigned short* __attribute__((always_inline))find_ram(){
  unsigned short sp;
  asm("mov r1, %0": "=m" (sp));
  /*
    mov #640, SP ;  #0x0280
    if (sp <= 0x1100 || sp>=0x38ff) 
    {
    unsigned short  new_sp = 0x0280;
    asm volatile("mov.w %0, r1\n\t"		\
    :: "r"(new_sp) ); 
    return (unsigned short*)new_sp;
    }
  */
  return (sp - RAM_REQUIREMENT > 0x1100)? (unsigned short*)(sp - 80) : (unsigned short*)(0x3800); 
}





/** This function is used for cloning. It reads the image header information corresponding to 
*   to the order specified in the update protocoll.
*   @param swupdate instance that holds a refernce to an ELF object containing 
*          header information.
*/

void image_header_read_word(struct swupdate *this){
  switch (this->elfe->wich_word){
  case 0: this->priv_word = this->elfe->nr;
    this->elfe->wich_word++;
    break;
  case 1: this->priv_word = this->elfe->start_addresses[this->elfe->segment_counter];
    this->elfe->wich_word++;
    break;
  case 2: this->priv_word = this->elfe->lengths[this->elfe->segment_counter];
    this->elfe->wich_word--;
    break;        
  }      
}





/** This function is used for cloning. It reads the segment data from the program memory.
*   @param swupdate instance that holds a refernce to an ELF object containing 
*          header information.
*/

void program_memory_read(struct swupdate *this){
  unsigned short tmp_address = this->elfe->start_addresses[this->elfe->segment_counter] 
    + this->elfe->inner_segment_counter;
  this->elfe->inner_segment_counter += this->datalen;
  if (this->elfe->lengths[this->elfe->segment_counter]<=this->elfe->inner_segment_counter){
    this->elfe->inner_segment_counter = 0;
  }
  memcpy(this->priv_data_buf, (unsigned short*)tmp_address, this->datalen);
}





/** This function writes a byte to the programm memory. Its code is executed from RAM.
*   @param The destination address and the value that will be written
*/

inline void __attribute__((always_inline)) flash_write_byte(unsigned short *dst, char value){
  
  FCTL2 = FWKEY | FSSEL0 | 20;        //clock source is MCLK, divisor is 20
  do
    {
      _NOP();
    } while(FCTL3 & 0x0001);            // wait for BUSY to reset
  
  FCTL3 = FWKEY;                      // reset the LOCK bit to enable program/erase
  FCTL1 = FWKEY | WRT;                // set WRT for single acces
  
  *dst = value;                       // do the write as a byte
  do
    {
      _NOP();
    } while(FCTL3 & 0x0001);            // wait for BUSY to reset
  
  
  return;
}




/** This function sets Chipselect of the SPI Bus to the external flash.
 *   CC1100 and the AT45DB161B are both connected to the SPI Bus.
 *   After the MSP ( Master ) did set the STE ( Chip Select Enable ) Signal 
 *   to the external flash ( SLAVE ), the slave ist able to transmit ( e.g. file contents )
 *   and reveive data ( e.g. a Flash Command ) from the SPI Bus.
 *   @param GPIOPin_t instance ( 4.5 )
 @param value for the chip select
*/
inline void __attribute__((always_inline)) gpio_set_op_(GPIOPin_t_ *pin, int value) {
  
  if(value) {((volatile unsigned char *)(unsigned int)pin->base)[1] |= pin->mask;
    
  }
  else {((volatile unsigned char *)(unsigned int)pin->base)[1] &= ~pin->mask;
    
  }
  
}    



/** This function sends a command over the SPI Bus and 
 *   reads the data sent from external flash controller after
 *   he has processed the command.
 *   @param *data_tx constant pointer to the FLASH COMMAND
 *   @param *data_rx receive buffer for the expect data
 *   @param len the expected length of the data
 */
inline void __attribute__((always_inline)) __spi_xf  (const char *data_tx, char *data_rx, int len) {
  while(len--) {
    U1TXBUF = data_tx?(*(data_tx++)):0;
    while(!(U1TCTL & TXEPT));
    if(data_rx) *(data_rx++) = U1RXBUF;
  }
}




/** This function sets up a special command ( PAGE READ )to the flash controller.
 *   
 *   @param *command constant pointer to the FLASH COMMAND
 *   @param *block_counter the block containing the requested page
 *   @param page counter the pagenumber inside the block ( 0 or 1 )
 */
inline void __attribute__((always_inline)) set_command (unsigned char *command, int block_counter, int page_counter){
  int page = block_counter*FLASH_BLOCKSIZE+page_counter;
  command[0] = FLASH_MAIN_PAGE_READ;
  command[1] = (page) >> 6;
  command[2] = (page) << 2; 
  command[3] = (0);
  
}




/** This function reads the filenumbers of the two files 
 *   containing the image header and the segment data of the
 *   new image.
 *   Before reprogramming, and especially after reset in the recovery case,
 *   we dont have that information in RAM. There might be several 
 *   program images on the external flash. When a program is running, 
 *   the fileforfile holds the filenumbers corresponding to that actual programm.
 *   The filenumbers will be changed right before a new installation.
 *   @param *command constant pointer to the FLASH COMMAND
 *   @param *block_counter the block containing the requested page
 *   @param page counter the pagenumber inside the block ( 0 or 1 )
 */
inline void __attribute__((always_inline)) file_for_file(SPIDevice_t_ *AT45, unsigned char *command, int *res){
  set_command(command, FILE_FOR_FILE, 0);  
  gpio_set_op_(&(AT45->pin_cs), 0);
  __spi_xf(command, NULL, 8);
  char tmp[2];
  __spi_xf(NULL, &tmp[0], 1);
  __spi_xf(NULL, &tmp[1], 1);
  res[0] = *((int*)tmp);
  __spi_xf(NULL, &tmp[0], 1);
  __spi_xf(NULL, &tmp[1], 1);
  res[1] = *((int*)tmp);
  gpio_set_op_(&(AT45->pin_cs), 1);
}



/** This function scratches the image header information 
 *   from the AT45DB161B.
 *   The function can be called from RAM during 
 *   reprogramming, cause it is declared inline 
 *   and doesn't make use of libraries.
 *   @param *AT45 SPIDevice instance - actually, 
 *   containing only the PIN connected to the AT45DB. 
 *   @param *command constant pointer to the FLASH COMMAND
 *   @param *sw_upd swupdate instance wich has a 
 *   struct elf as member to store the header information in.
 */
inline void __attribute__((always_inline)) get_elf_info(SPIDevice_t_ *AT45, unsigned char *command, struct swupdate *sw_upd, int f1, int f2)
{
  sw_upd->file_for_image_header = f1;
  sw_upd->file_for_segment_data = f2;
  set_command(command, sw_upd->file_for_image_header, 0);  
  gpio_set_op_(&(AT45->pin_cs), 0);
  __spi_xf(command, NULL, 8);
  char tmp[2];
  __spi_xf(NULL, &tmp[0], 1);
  __spi_xf(NULL, &tmp[1], 1);
  int number_of_segments = *((int*)tmp);
  sw_upd->elfe->nr = number_of_segments;
  __spi_xf(NULL, &tmp[0], 1);
  __spi_xf(NULL, &tmp[1], 1);
  sw_upd->elfe->elf_size = *((unsigned int*)tmp);
  int i;
  for (i=0; i< sw_upd->elfe->nr; i++){
    __spi_xf(NULL, &tmp[0], 1);
    __spi_xf(NULL, &tmp[1], 1);
    sw_upd->elfe->start_addresses[i] = *((int*)tmp);
  }   
  for (i=0; i< sw_upd->elfe->nr; i++){
    __spi_xf(NULL, &tmp[0], 1);
    __spi_xf(NULL, &tmp[1], 1);
    sw_upd->elfe->lengths[i] = *((int*)tmp);
  }   
  __spi_xf(NULL, tmp, 2);
  sw_upd->elfe->no_of_used_blocks = *((int*)tmp);
  for (i=0; i< sw_upd->elfe->no_of_used_blocks; i++){
    __spi_xf(NULL, &tmp[0], 1);
    __spi_xf(NULL, &tmp[1], 1);
    sw_upd->elfe->block_list[i] = *((int*)tmp);
#if DEBUG_2
    SHOW_DEC(sw_upd->elfe->block_list[i]);
#endif
  }
  gpio_set_op_(&(AT45->pin_cs), 1);
#if DEBUG_2
  SHOW_DEC(sw_upd->file_for_image_header);   
  SHOW_DEC(sw_upd->file_for_segment_data);
  SHOW_DEC(sw_upd->elfe->elf_size);
  SHOW_DEC(sw_upd->elfe->nr);
#endif
}




/** This function scratches the content of a page from the external flash and 
*   writes its content to the program memory. The correct address within the last 
*   is determined by the scratch_status and the elf object.
*   @param scratch_status instance containg information how many byte are left to write etc.
*   @param elf instance containing the image header information.
*   @param spi device instance
*   @param command for the flash controller
*/

inline void __attribute__((always_inline)) scratch_page(SPIDevice_t_ *AT45, unsigned char *command, struct elf *e_i, struct scratch_status *s_t){
  unsigned char cd;
  int flash_page_read_length;
#if DEBUG_2
  SHOW_DEC(s_t->bytes_left_to_read);
#endif
  if (s_t->bytes_left_to_read > FLASH_PAGESIZE) {
    flash_page_read_length = FLASH_PAGESIZE;
    if (s_t->page_counter==1) { flash_page_read_length = flash_page_read_length - 2;} //subtract the two byte pointer at end of block
  }	
  else { 
    flash_page_read_length = s_t->bytes_left_to_read; 
  }
  set_command(command, e_i->block_list[s_t->block_counter], s_t->page_counter);
  gpio_set_op_(&(AT45->pin_cs), 0);
  __spi_xf(command, NULL, 8);
#if DEBUG_2
  SHOW_DEC(e_i->block_list[s_t->block_counter]);
#endif
  int i;
  
  for (i = 0; i < flash_page_read_length; i++) {
    __spi_xf(NULL, &cd, 1);
    if (s_t->actual_segment_data_count>=e_i->lengths[s_t->actual_segment_nr]){
      s_t->actual_segment_nr++;
      s_t->actual_segment_data_count = 0;
    }
    unsigned short *address = (unsigned short *)(e_i->start_addresses[s_t->actual_segment_nr] + s_t->actual_segment_data_count);
    
    
    if (address==(unsigned short*)0xFFFE){
      e_i->reset_vec_adr[0] = cd;
      cd = (e_i->start_addresses[0])&0xff;
    }
    else if (address==(unsigned short*)0xFFFF) {
      e_i->reset_vec_adr[1] = cd;   
      cd = (e_i->start_addresses[0]) >> 8;
    }
	
#if !DEBUG_2
    flash_write_byte(address, cd);
#else 
    printf ("writing byte nr %i of segment %i at %.2x with value %.2x\n",
	    s_t->actual_segment_data_count, s_t->actual_segment_nr,
	    e_i->start_addresses[s_t->actual_segment_nr] +
	    s_t->actual_segment_data_count, cd);
#endif
    s_t->actual_segment_data_count++;  
  }
  gpio_set_op_(&(AT45->pin_cs), 1);
  s_t->bytes_left_to_read -= flash_page_read_length;
#if DEBUG_2
  SHOW_DEC(e_i->elf_size);
#endif
}





/** This function scratches the content of a block from the external flash.
*   A filesystem block contains of two pages.
*   @param scratch_status instance containg information how many byte are left to write etc.
*   @param elf instance containing the image header information.
*   @param command for the flash controller
*   @param spi device
*/

inline void scratch_block(SPIDevice_t_ *AT45, unsigned char *command, struct elf *elfe, struct scratch_status *s_t){
  for (s_t->page_counter = 0; s_t->page_counter < FLASH_BLOCKSIZE; s_t->page_counter++) {
    
    scratch_page(AT45, command, elfe, s_t);      
  }
}





/** This function scratches a program image a file on the external flash.
*   @param scratch_status instance containg information how many byte are left to write etc.
*   @param elf instance containing the image header information.
*   @param command for the flash controller
*   @param spi device
*/

inline void __attribute__((always_inline)) scratch_from_ext_flash(SPIDevice_t_ *AT45, unsigned char *command, struct elf *elfe, struct scratch_status *s_t){
  s_t->actual_segment_data_count = 0;
  s_t->actual_segment_nr = 0;
  s_t->bytes_left_to_read = elfe->elf_size;
  for (s_t->block_counter = 0; s_t->block_counter < elfe->no_of_used_blocks; s_t->block_counter++){
    scratch_block(AT45, command, elfe, s_t);
  }
#if (TESTCASE == POWERLOSS)
  P6DIR |= 0x16;
  unsigned long d = 1000000;
  for (;d>0;d--) {
    _NOP();
    P6OUT &= 0x10;}
#endif
}





/** This function writes the elf header information to a file on the external flash.
*   @param pointer to the swupdate instance that holds again a reference to an elf object 
*          with  the image header. 
*/

void save_elf_header(struct swupdate *sw_upd){
  SHOW(saving elf header..);
  sw_upd->elfe->no_of_used_blocks = (sw_upd->elfe->elf_size + (FLASH_PAGESIZE*FLASH_BLOCKSIZE - 1))/
    (FLASH_PAGESIZE * FLASH_BLOCKSIZE);
  while(!getResourceFor(&FLASH_RESOURCE, 20000));
  if (sw_upd->update_command != JUST_STORE_AN_IMAGE){
    delete(FILE_FOR_FILE);    
    SHOW(file_for_file  deleted); 
    if (append(FILE_FOR_FILE,(unsigned char*)&(sw_upd->file_for_image_header),sizeof(int))!=sizeof(int)){ printf("error\n"); }
    if (append(FILE_FOR_FILE,(unsigned char*)&(sw_upd->file_for_segment_data),sizeof(int))!=sizeof(int)){ printf("error\n"); }
    SHOW(updated my own program header);
  }
  if (sw_upd->src_node_id != sw_upd->dst_node_id){
    if (append(sw_upd->file_for_image_header,(unsigned char*)&(sw_upd->elfe->nr),sizeof(int))!=sizeof(int)){ printf("error\n"); }
    if (append(sw_upd->file_for_image_header,(unsigned char*)&(sw_upd->elfe->elf_size),sizeof(unsigned int))!=sizeof(unsigned int)){ printf("error\n"); }
    if (append(sw_upd->file_for_image_header,(unsigned char*)sw_upd->elfe->start_addresses,sw_upd->elfe->nr*sizeof(unsigned int))!=sw_upd->elfe->nr*sizeof(unsigned int)){ printf("error\n"); }
    if (append(sw_upd->file_for_image_header,(unsigned char*)sw_upd->elfe->lengths,sw_upd->elfe->nr*sizeof(unsigned int))!=sw_upd->elfe->nr*sizeof(unsigned int)){ printf("error\n"); }
    if (append(sw_upd->file_for_image_header,(unsigned char*)&(sw_upd->elfe->no_of_used_blocks),sizeof(int))!=sizeof(int)){ printf("error\n"); }
    if (append(sw_upd->file_for_image_header,(unsigned char*)sw_upd->elfe->block_list,sw_upd->elfe->no_of_used_blocks*sizeof(unsigned int))!=sw_upd->elfe->no_of_used_blocks*sizeof(unsigned int)){ printf("error\n"); }
  }
  sync();
  releaseResource(&FLASH_RESOURCE);
  
}




/** This function writes a block of image data to a file on the external flash.
*   The lower level function is linked against the general purpose read function of the abstract 
*   level when the node is the Destination of the Update command. 
*   @param pointer to the swupdate instance that holds the input buffer. 
*/

void at45_write(struct swupdate *this){
  while(!getResourceFor(&FLASH_RESOURCE, 20000));
  if (append(this->file_for_segment_data, this->priv_data_buf, this->datalen)!=this->datalen){ printf("error\n"); }
  sync();
  releaseResource(&FLASH_RESOURCE);
}




/** This function deletes the files for the segment data and the image header before 
*   the node retrieves a new image that will be stored in these two files. 
*   @param pointer to the swupdate instance containing the filenumbers. 
*/

void setup_external_flash(struct swupdate *sw_upd){
  while(!getResourceFor(&FLASH_RESOURCE, 20000));
  if(sw_upd->src_node_id != sw_upd->dst_node_id){
    delete(sw_upd->file_for_image_header);
    delete(sw_upd->file_for_segment_data);
  }
  sync();
  releaseResource(&FLASH_RESOURCE);
  
  
}



/** This function writes the a dirty flag to the information memory to 
*   to an address that is unused by now. The value 1 means that an error 
*   occured during reprogramming. 0 indicates that reprogramming was correct. 
*   @param value value of the bit. 
*/

void __attribute__((always_inline))wr_dirty_flag(char value)
{
  char *dirty_flag_p = (char*) 0x1090;
  FCTL2 = FWKEY + FSSEL_1 + 12;  	// Set Up Timing Generator
  FCTL3 = FWKEY;                 	// Make Certain that LOCK is 
  FCTL1 = FWKEY + WRT;
  *dirty_flag_p = value;
  while (FCTL3 & BUSY);
  return;       
}



/** This function updates the checksum of the information memory 
*   after the  modification (for example by setting the dirty bit).
*   @param crc The new value of the crc
*/

void __attribute__((always_inline)) update_crc(unsigned short crc){
  char *pos = (char*) 0x10FE;
  FCTL2 = FWKEY + FSSEL_1 + 12;  	// Set Up Timing Generator
  FCTL3 = FWKEY;                 	// Make Certain that LOCK is 
  FCTL1 = FWKEY + WRT;
  *pos = crc;
  while (FCTL3 & BUSY);
  return;       
  
}



/** This function copies the information memory to a buffer in RAM. 
*   The temporary copy is used to modify the information memory. 
*   @param *buf pointer to the buffer where the infomem should be stored in. 
*   @param len length of the infomem
*/

void ramcpy_infomem(unsigned char *buf, unsigned int len)
{
  unsigned int i;
  unsigned char *p = (unsigned char*) 0x1080;
  
  for(i=0;i<len-1;)
    {
      buf[i] = *(p+1);
      buf[i+1] = *p;
      p=p+2;
      i=i+2;
    }
}




/** This function makes a MASS erase (see MSP430 users guide) 
*   before the program memory is written.
*/

inline void __attribute__((always_inline)) erase_everything(void)
 
/*must be run from RAM.
  Default address of jtag erase is 0xfffe within a segment
  Default erase type is ERASE_ALL
  Default length of consecuting segments which shall be erased 
  is 2.
  Look in jtag.py in def memwrite for details*/
  
{
#if DEBUG_2
    unsigned short pc_count;
    asm("mov r0, %0": "=m" (pc_count));
    SHOW_HEX(pc_count); 
#else
    char *p = (char *) 0x4000;
  WDTCTL = WDTPW + WDTHOLD;
  FCTL2 = FWKEY + FSSEL1 + FN0;
  FCTL3 = FWKEY; 	//clear LOCK
  FCTL1 = FWKEY + MERAS;
  *p = 0xFF;  //erased flash locations hold this value 
  while (FCTL3 & 0x0001);
  return;
#endif
}

 

/** This function returns the code length of a function when it is called 
*   by a function pointer and an end label of a function. It is used when code is 
*   to be loaded for execution in RAM.
*   @param *start pointer to the start of the function
*   @param * stop pointer to the end of the function
*/

unsigned short code_len(const unsigned short *start, const unsigned short *stop){
  return stop - start;
}




/** This function copies code from the flash into the RAM when it is called 
*   by the begin and the end of that function code. 
*   @param *src i.e. pointer to code in in program memory. 
*   @param *dest i.e. pointer to the place in RAM. 
*   @param len i.e.  length of the code 
*/

inline void __attribute__((always_inline)) ramcpy(const unsigned short *src, unsigned short *dest, unsigned short len) {
  len >>= 1;
  while (len) {
    *dest++ = *src++;
    len--;
  }
}





/** This function does the reprogramming of the flash. 
*   It is completely independent from the OS. 
*   All interrupts are deactivated. It has no function parameters, 
*   and uses no variables stored in ROM. 
*   It has got complete control over the CPU, the ROM and the RAM. 
*   The image header info and the segment data is retrieved from files in the 
*   external flash and written to the programm memory. The function copies itself 
*   into RAM and calls itsself there for execution from RAM. 
*   After the new image is written, it causes a reset to give control to that 
*   new program. 
*/

void __attribute__((noreturn)) program_flash(){
  _DINT();
  _NOP(); //Empfehlung User's Guide page 73
  
  if (!running_from_ram()){
    unsigned short len = (unsigned short)end_label_program_flash - (unsigned short)(program_flash);
    unsigned short *destFunc = find_ram();
    ramcpy((unsigned short *)program_flash, destFunc, len);
    void (*ramcode)() = (void (*)())destFunc;
    ramcode();
  }  
  
  erase_everything();
  unsigned char command[8];
  SPIDevice_t_ AT45 = {
    .pin_cs = { .base= P5IN_, .mask = (1<<4), },
  };
  struct elf elfe;
  struct swupdate sw_upd;
  sw_upd.elfe = &elfe;
  struct scratch_status s_t;
  int file_info[2];
  file_for_file(&AT45, command, file_info);
  get_elf_info(&AT45, command, &sw_upd, file_info[0], file_info[1]);
 
// the reset vector is modified temporarily to point to the recovery routine 
// in case of an write error. 
#if !DEBUG_2
  flash_write_byte((unsigned short*)0xfffe,(elfe.start_addresses[0])>>8); 
  flash_write_byte((unsigned short*)0xffff,(elfe.start_addresses[0])&0xff); 
#endif

  
  scratch_from_ext_flash(&AT45, command, &elfe, &s_t);

// the old value of the reset vector is restored. Here, at the end of the reprogramming 
// word, all image data was written correctly  
#if !DEBUG_2  
  flash_write_byte((unsigned short*)0xfffe,elfe.reset_vec_adr[0]); 
  flash_write_byte((unsigned short*)0xffff,elfe.reset_vec_adr[1]); 
#endif    
  

 // causing a reset by violating the Passwort of the watchdog timer 
  WDTCTL = 0;
  
  // function does not return
  __NORETURN__;
}
void end_label_program_flash(){};
